<?php

namespace app\admin\command;

use app\admin\model\park\Hub;
use app\admin\model\park\ParkingPlace;
use Swoole\WebSocket\Server;
use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\Db;

class Park extends Command
{
    protected $server;
    protected $tcp_server;
    protected $output;

    protected function configure()
    {
        $this->setName('park')
            ->setDescription('start parking service');
    }

    protected function execute(Input $input, Output $output)
    {
        $this->output = $output;
        if ($this->startService()) {
            $output->info("Start Successed!");
        } else {
            $output->info("Start Failed:" . swoole_strerror(swoole_last_error(), 9));
        }
    }

    /**
     * 启动服务
     */
    protected function startService()
    {
        $this->server = new Server("0.0.0.0", 9999);
        $this->server->on('start', function ($server) {
            echo "park service started\n";
        });
        $this->server->on('open', array($this, 'websocketOpen'));
        $this->server->on('message', array($this, 'websocketMessage'));
        $this->server->on('close', array($this, 'websocketClose'));

        //多监听一个TCP端口，对外开启TCP服务，并设置TCP服务器的回调
        $this->tcp_server = $this->server->listen('127.0.0.1', 9998, SWOOLE_SOCK_TCP);
        //默认新监听的端口 9999 会继承主服务器的设置，也是 HTTP 协议
        //需要调用 set 方法覆盖主服务器的设置
        $this->tcp_server->set(array(
            'open_eof_check' => true,
            'package_eof' => "\r\n",
        ));
        // $client->set(array(
        //     'open_eof_check' => true,
        //     'package_eof' => "\r\n",
        // ));
        $this->tcp_server->on('receive', array($this, 'tcpMessage'));

        return $this->server->start();
    }

    public function websocketOpen(Server $server, $request)
    {
        echo "server: handshake success with fd{$request->fd}\n";
    }

    public function websocketClose(Server $server, $fd)
    {
        echo "client {$fd} closed\n";
        $model = new \app\admin\model\park\Hub;
        $hub = $model->where('fd', $fd)->find();
        if ($hub) {
            $hub->online_status = 'offline';
            $hub->fd = 0;
            $hub->save();
            $devices = ParkingPlace::where('hub_id', $hub->id)->select();
            foreach ($devices as $device) {
                $device->online_status = 'offline';
                $device->save();
            }
            return;
        }
    }

    // 上线先发auth 数据，指明是谁
    public function websocketMessage(Server $server, $frame)
    {
        $this->output->info("receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n");
        // $server->push($frame->fd, "this is server");
        if ($frame->data) {
            $msg = explode(' ', $frame->data);
            if ($msg[0] == 'auth') {
                $model = new \app\admin\model\park\Hub;
                $hub = $model->where('id', $msg[1])->find();
                if ($hub) {
                    // 新hub上线，踢下旧的
                    if ($hub->fd && $hub->fd != $frame->fd) {
                        $server->close($hub->fd);
                    }
                    $hub->fd = $frame->fd;
                    $hub->online_status = 'online';
                    $hub->save();
                    $server->push($frame->fd, "hello {$msg[1]}");
                }
            } else {
                $data = json_decode($msg[1]);
                $model = new \app\admin\model\park\Hub;
                $hub = $model->where('fd', $frame->fd)->find();
                if (!$hub) {
                    $this->output->warning("can not find fd={$frame->fd}");
                    return;
                }
                

                // 打开完成回应
                if ($msg[0] == "connected") {
                    $devices = ParkingPlace::where('hub_id', $hub->id)->where('device_ip', $data->ip)->select();
                    $this->output->info("connected hub_id={$hub->id} ip={$data->ip} found:" . count($devices));
                    if (count($devices) > 0) {
                        foreach ($devices as $device) {
                            $device->online_status = 'online';
                            $device->save();
                        }
                    }
                } else if ($msg[0] == "received") { // 收到消息
                    $this->output->info("received from {$hub->id}: {$data->key} {$data->body}");

                } else if ($msg[0] == "closed") { // 关闭回应
                    $devices = ParkingPlace::where('hub_id', $hub->id)->where('device_ip', $data->ip)->select();
                    $this->output->info("closed hub_id={$hub->id} ip={$data->ip} found:" . count($devices));
                    if (count($devices) > 0) {
                        foreach ($devices as $device) {
                            $device->online_status = 'offline';
                            $device->save();
                        }
                    }
                }
            }
        }
    }

    public function tcpMessage($server, $fd, $threadId, $data)
    {
        $this->output->info("tcp server receive from {$fd}:{$data}\n");
        if ($data) {
            $msg = explode(' ', $data);
            $model = Db::name("parking_place")->where('id', trim($msg[1]))->find();
            if ($model) {
                $hub = Db::name("hub")->where('hub_id', $model->hub_id)->find();

                if ($msg[0] == 'close') {
                    if (!$hub || $hub->online_status != 'online' || !$this->server->exist($hub->fd)) {
                        $this->output->warning("{$hub->id} hub offline");
                        $server->push($fd, 'hub offline');
                    } else {
                        // TODO:发送关闭地桩指令
                        $msg = "";
                        //$this->server->send($hub->fd, "send {$hub->ip} {$msg}");
                        $this->output->info("send message to {$hub->id} {$hub->ip} success: {$msg}");
                        $server->push($fd, 'success');
                    }
                } else if ($msg[0] == 'open') {
                    if (!$hub || $hub->online_status != 'online' || !$this->server->exist($hub->fd)) {
                        $this->output->warning("{$hub->id} hub offline");
                        $server->push($fd, 'hub offline');
                    } else {
                        // TODO:发送打开地桩指令
                        $msg = "";
                        //$this->server->send($hub->fd, "send {$hub->ip} {$msg}");
                        $this->output->info("send message to {$hub->id} {$hub->ip} success: {$msg}");
                        $server->push($fd, 'success');
                    }
                }
            }

        }
    }
}
